package org.rpgmapper;

import com.jme3.app.SimpleApplication;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.KeyInput;
import com.jme3.input.TouchInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.TouchListener;
import com.jme3.input.controls.TouchTrigger;
import com.jme3.input.event.TouchEvent;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.niftygui.NiftyJmeDisplay;
import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import com.jme3.texture.Texture;
import com.jme3.ui.Picture;
import com.jme3.util.SkyFactory;
import com.jme3.util.TempVars;
import de.lessvoid.nifty.Nifty;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.nwn.jme3.NwnModelLoader;
import org.rpgmapper.character.CharacterManager;
import org.rpgmapper.character.CharacterModel;
import org.rpgmapper.character.StandardCharacterModel;
import org.rpgmapper.tools.AppContext;
import org.rpgmapper.bo.Character;
import org.rpgmapper.bo.Hero;
import org.rpgmapper.bo.Stat;
import org.rpgmapper.bo.rules.ModifierRule;
import org.rpgmapper.character.NWNCharacterModel;
import org.rpgmapper.gui.CharacterInfoControl;
import org.rpgmapper.gui.DrawGuiController;
import org.rpgmapper.gui.MenuController;
import org.rpgmapper.gui.menu.MenuItem;
import org.rpgmapper.manager.MapManager;

/**
 * test
 * @author Remy Bouquet 
 */
public class JmeMain extends SimpleApplication {

    private static final Logger logger = Logger.getLogger(JmeMain.class.getName());
    private ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(2);
    private Future loadFuture = null;
    private boolean load = true;
    private Picture splash = new Picture("splash");
    private JmeInitListener initListener = null;

    public static void main(String[] args) {
        JmeMain app = new JmeMain();
//        AppSettings settings = new AppSettings(true);
//       // settings.setFrameRate(30);
//        app.setSettings(settings);
        //app.setShowSettings(false);
        app.start();

//        for (int i = 0; i < 255; i++) {
//            System.out.println(i+" "+(char)i);
//        }
    }

    @Override
    public void simpleInitApp() {
        // inputManager.clearMappings();
        //setDisplayStatView(false);
        // setDisplayFps(false);
        init();

//        float size = Math.min(cam.getWidth(), cam.getHeight());
//        splash.setHeight(size);
//        splash.setWidth(size);
//        splash.setImage(assetManager, "Interface/RPGMapper.jpg", false);
//        splash.setLocalTranslation(cam.getWidth() / 2f - size / 2f, 0, 0);
//        guiNode.attachChild(splash);
    }
    Callable<Void> loadingCallable = new Callable<Void>() {

        public Void call() {
            return init();
        }
    };
    Nifty nifty;

    private Void init() throws SecurityException {
        //        Node scene = (Node) assetManager.loadModel("Models/MapCity/MapCity.j3o");
        //        scene.getChild("Ground").removeFromParent();
        //
        NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,
                inputManager,
                audioRenderer,
                guiViewPort);
        nifty = niftyDisplay.getNifty();
        //nifty.fromXml("Interface/DrawGui.xml", "drawToolBox", new DrawGuiController());
        nifty.fromXml("Interface/menuLight.xml", "drawToolBox", new MenuController(), new DrawGuiController());
        //guiViewPort.addProcessor(niftyDisplay);
        // attach the nifty display to the gui view port as a processor
        // Create a new cam for the gui
        Camera guiCam = guiViewPort.getCamera().clone();
        ViewPort niftyViewPort = renderManager.createPostView("Nigty Gui", guiCam);
        niftyViewPort.setClearFlags(false, false, false);


        final MapManager mapManager = new MapManager(64, assetManager);

        mapManager.registerInput(inputManager);
        AppContext.init(assetManager, inputManager, mapManager, rootNode, guiNode, cam);

        Logger root = Logger.getLogger("");
        Handler[] handlers = root.getHandlers();
        for (int i = 0; i < handlers.length; i++) {
            if (handlers[i] instanceof ConsoleHandler) {
                ((ConsoleHandler) handlers[i]).setLevel(Level.WARNING);
            }
        }

        flyCam.setEnabled(false);
        //        flyCam.setMoveSpeed(20);
        //        flyCam.setDragToRotate(true);

        DirectionalLight light = new DirectionalLight();
        light.setColor(ColorRGBA.White);
        Vector3f lightDir = new Vector3f(37.020f, -64.974f, 42.684f).normalizeLocal();
        light.setDirection(lightDir);
        rootNode.addLight(light);
        AmbientLight al = new AmbientLight();
        al.setColor(ColorRGBA.White.mult(0.7f));
        rootNode.addLight(al);

         Character golem = new Hero(0, "Iron Golem");
        golem.setBase(Stat.Level, 4);
        golem.setBase(Stat.Str, 21);
        golem.setBase(Stat.Dex, 13);
        golem.setBase(Stat.Con, 16);
        golem.setBase(Stat.Int, 7);
        golem.setBase(Stat.Wis, 10);
        golem.setBase(Stat.Cha, 7);
        golem.setBase(Stat.Ref, 1);
        golem.setBase(Stat.For, 4);
        golem.setBase(Stat.Wil, 1);
        golem.setBase(Stat.MovePoints, 6);
        golem.setBase(Stat.BBA, 4);
        golem.setBase(Stat.AC, 4);
        golem.addRule(new ModifierRule(Stat.MovePoints, 2));
        golem.addRule(new ModifierRule(Stat.Init, 4));
        golem.setBase(Stat.MaxHitPoints, 50);
        golem.setBase(Stat.HitPoints, 50);
        golem.refreshRuleSet();


                   assetManager.registerLoader(NwnModelLoader.class, "mdl");
        
                CharacterModel model1 = new NWNCharacterModel(assetManager, "Models/IronGolem/Iron_Golem.mdl");
                final CharacterManager characterManager1 = new CharacterManager(model1,golem);
                mapManager.addCharacter(characterManager1);
                //   rootNode.attachChild(model.getNode());
                characterManager1.setCell(2080);



        Texture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg");
        Texture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg");
        Texture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg");
        Texture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg");
        Texture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg");
        Texture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg");
        Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down);




//        inputManager.addListener(new TouchListener() {
//
//            public void onTouch(String string, TouchEvent te, float f) {
//                // System.err.println(te.getType().name());
//
//                if (te.getType() == TouchEvent.Type.DOUBLETAP) {
//                    for (Iterator<CharacterManager> it = mapManager.characterIterator(); it.hasNext();) {
//                        CharacterManager cm = it.next();
//                        cm.getCharacter().setBase(Stat.HitPoints, cm.getCharacter().get(Stat.HitPoints) - 5);
//                    }
//
//                }
//            }
//        }, "touchMe");
//        inputManager.addListener(new ActionListener() {
//
//            public void onAction(String string, boolean bln, float f) {
//                if (string.equals("hit") && bln) {
//                    for (Iterator<CharacterManager> it = mapManager.characterIterator(); it.hasNext();) {
//                        CharacterManager cm = it.next();
//                        cm.getCharacter().setBase(Stat.HitPoints, cm.getCharacter().get(Stat.HitPoints) - 5);
//                    }
//                }
//            }
//        }, "hit");
//        inputManager.addMapping("touchMe", new TouchTrigger(TouchInput.ALL));
//        inputManager.addMapping("hit", new KeyTrigger(KeyInput.KEY_H));


        rootNode.attachChild(mapManager.getGroundNode());
        rootNode.attachChild(mapManager.getCharactersNode());
        rootNode.attachChild(sky);
        niftyViewPort.addProcessor(niftyDisplay);

        ///preinit model
        assetManager.loadModel("Models/Pawn/Pawn.j3o");

        createCharacter();
        if (initListener != null) {
            initListener.onInit();
        }

        return null;
    }

    public JmeInitListener getInitListener() {
        return initListener;
    }

    public void setInitListener(JmeInitListener initListener) {
        this.initListener = initListener;
    }
    CharacterModel model;

    private void createCharacter() {
        Character foa = new Hero(0, "Iron Golem");
        foa.setBase(Stat.Level, 4);
        foa.setBase(Stat.Str, 21);
        foa.setBase(Stat.Dex, 13);
        foa.setBase(Stat.Con, 16);
        foa.setBase(Stat.Int, 7);
        foa.setBase(Stat.Wis, 10);
        foa.setBase(Stat.Cha, 7);
        foa.setBase(Stat.Ref, 1);
        foa.setBase(Stat.For, 4);
        foa.setBase(Stat.Wil, 1);
        foa.setBase(Stat.MovePoints, 6);
        foa.setBase(Stat.BBA, 4);
        foa.setBase(Stat.AC, 4);
        foa.addRule(new ModifierRule(Stat.MovePoints, 2));
        foa.addRule(new ModifierRule(Stat.Init, 4));
        foa.setBase(Stat.MaxHitPoints, 50);
        foa.setBase(Stat.HitPoints, 50);
        foa.refreshRuleSet();

    //    model = new StandardCharacterModel(assetManager, "Models/Pawn/Pawn.j3o");
           assetManager.registerLoader(NwnModelLoader.class, "mdl");
        
                model = new NWNCharacterModel(assetManager, "Models/IronGolem/Iron_Golem.mdl");
//                final CharacterManager characterManager1 = new CharacterManager(model1,golem);
//                mapManager.addCharacter(characterManager1);
//                //   rootNode.attachChild(model.getNode());
//                characterManager1.setCell(2080);


        final CharacterManager characterManager = new CharacterManager(model, foa);
        AppContext.getMapManager().addCharacter(characterManager);
        // System.out.println("coords : " + x + "," + y);
        characterManager.setCell(2072);

        model.getNode().addControl(new CharacterInfoControl(foa));

//        rootMenu = new MenuItem("rootMenu", null);
//        MenuItem attackMenu = new MenuItem("attackMenu", "Textures/Icons/Attack.png" );
//      
//        MenuItem spellMenu = new MenuItem("spellMenu", "Textures/Icons/Spell.png");
//        MenuItem moveMenu = new MenuItem("moveMenu", "Textures/Icons/Walk.png");
//        moveMenu.setListener(new MenuItemListener() {
//
//            public void onClick() {
//                System.out.println("Move");
//                appContext.getMapManager().setSelectedCharacter(characterManager);
//                JmeMain.getApplicationContext().getSelectionManager().setMode(SelectionManager.Mode.Cell);
//                characterManager.startMove();
//                rootMenu.expand(false);
//            }
//        }); 
//        
//        MenuItem attackMenu2 = new MenuItem("attackMenu2", "Textures/Icons/Attack.png" );
//        MenuItem spellMenu2 = new MenuItem("spellMenu2", "Textures/Icons/Spell.png");
//        MenuItem moveMenu2 = new MenuItem("moveMenu2", "Textures/Icons/Walk.png");
//        
//        attackMenu.addItem(attackMenu2);
//        attackMenu.addItem(spellMenu2);
//        attackMenu.addItem(moveMenu2);
//        
//        rootMenu.addItem(attackMenu);
//        rootMenu.addItem(spellMenu);
//        rootMenu.addItem(moveMenu);
//       
//        guiNode.attachChild(rootMenu.getMainNode());   


        inputManager.addListener(new ActionListener() {

            public void onAction(String string, boolean bln, float f) {
                if (string.equals("hit") && bln) {
                    // rootMenu.expand(!rootMenu.isExpanded());     
                    if (!show) {
                        nifty.gotoScreen("menu");
                        show = true;
                    } else {
                        nifty.gotoScreen("drawToolBox");
                        show = false;
                    }
                }
            }
        }, "hit");

        inputManager.addListener(new TouchListener() {

            public void onTouch(String string, TouchEvent te, float f) {
                // System.err.println(te.getType().name());

                if (te.getType() == TouchEvent.Type.SHOWPRESS) {
                    //rootMenu.expand(!rootMenu.isExpanded()); 
                    if (!show) {
                        nifty.gotoScreen("menu");
                        show = true;
                    } else {
                        nifty.gotoScreen("drawToolBox");
                        show = false;
                    }
                }
            }
        }, "touchMe");

        inputManager.addMapping("hit", new KeyTrigger(KeyInput.KEY_H));
        inputManager.addMapping("touchMe", new TouchTrigger(TouchInput.ALL));

    }
    boolean show = false;
    MenuItem rootMenu;

//
//    private Character createFiend() {
//        Character goblin = new Foe(1, "Goblin");
//        goblin.setBase(Stat.Level, 1);
//        goblin.setBase(Stat.Size, -1);
//        goblin.setBase(Stat.Str, 11);
//        goblin.setBase(Stat.Dex, 15);
//        goblin.setBase(Stat.Con, 12);
//        goblin.setBase(Stat.Int, 10);
//        goblin.setBase(Stat.Wis, 9);
//        goblin.setBase(Stat.Cha, 6);
//        goblin.setBase(Stat.Ref, 2);
//        goblin.setBase(Stat.For, 3);
//        goblin.setBase(Stat.Wil, -1);
//        goblin.setBase(Stat.MovePoints, 6);
//        goblin.setBase(Stat.BBA, 1);
//        goblin.setBase(Stat.AC, 3);
//        goblin.addRule(new ModifierRule(Stat.Init, 4));
//        goblin.setBase(Stat.MaxHitPoints, 6);
//        goblin.setBase(Stat.HitPoints, 6);
//        goblin.refreshRuleSet();
//        return goblin;
//    }
    @Override
    public void simpleUpdate(float tpf) {
        // rootMenu.getMainNode().setLocalTranslation(cam.getScreenCoordinates(model.getNode().getWorldTranslation()));
//        if (load) {
//            if (loadFuture == null) {
//                //if we have not started loading yet, submit the Callable to the executor
//                loadFuture = exec.submit(loadingCallable);
//            }
//            //check if the execution on the other thread is done
//            if (loadFuture.isDone()) {
//                //these calls have to be done on the update loop thread, 
//                //especially attaching the terrain to the rootNode
//                //after it is attached, it's managed by the update loop thread 
//                // and may not be modified from any other thread anymore!
//                splash.removeFromParent();
//                load = false;
//            }
//        }
    }

    public void enlistCharacter(final Character c, final float x, final float y) {
        enqueue(new Callable() {

            public Object call() throws Exception {

                CharacterModel model = new StandardCharacterModel(assetManager, "Models/Pawn/Pawn.j3o");
                if (c.getSide() == 0) {
                    model.getNode().setMaterial(assetManager.loadMaterial("Models/Pawn/Foe.j3m"));
                }
                final CharacterManager characterManager = new CharacterManager(model, c);
                AppContext.getMapManager().addCharacter(characterManager);
                // System.out.println("coords : " + x + "," + y);
                characterManager.setCell(pick(x, y));

                model.getNode().addControl(new CharacterInfoControl(c));
                // System.out.println("added "+ c.getName());
                return null;
            }
        });
    }

    public void removeCharacter(final Character c) {
        enqueue(new Callable() {

            public Object call() throws Exception {


                final CharacterManager characterManager = AppContext.getMapManager().getCharacter(c.getId() + "");
                AppContext.getMapManager().removeCharacter(characterManager);

                CharacterInfoControl control = characterManager.getModel().getControl(CharacterInfoControl.class);
                control.dispose();
                characterManager.getModel().removeControl(control);

                // System.out.println("added "+ c.getName());
                return null;
            }
        });
    }

    private int pick(float x, float y) {
        TempVars vars = TempVars.get();
        Vector2f v2 = vars.vect2d;
        v2.set(x, y);
        Vector3f origin = cam.getWorldCoordinates(v2, 0.0f, vars.vect1);
        Vector3f direction = cam.getWorldCoordinates(v2, 0.3f, vars.vect2);
        direction.subtractLocal(origin).normalizeLocal();

        Ray ray = new Ray(origin, direction);
        CollisionResults results = new CollisionResults();
        Vector3f contactPoint = null;
        AppContext.getMapManager().getGroundNode().collideWith(ray, results);

        int index = 0;
        if (results.size() > 0) {
            CollisionResult closest = results.getClosestCollision();
            contactPoint = closest.getContactPoint();
            index = AppContext.getMapManager().getIndex(contactPoint);
        } else {
            index = -1;
        }

        vars.release();
        return index;

    }
}
